package cn.edik.plugin.dubbo.core;

import cn.edik.plugin.dubbo.annotation.Reference;
import cn.edik.plugin.dubbo.annotation.Service;
import cn.edik.plugin.dubbo.exception.JFinalDubboPluginException;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.Prop;
import com.jfinal.kit.StrKit;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.ServiceConfig;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Author    : edik
 * CreateTime: 2019-02-14 15:42
 **/
public class JFinalDubbo {

    private static DubboConfigFactory dubboConfigFactory;

    //    private static String classPath = PathKit.getRootClassPath();
    private static String classPath = "";
    private static String dubbo_base_package = "";

    private static ConcurrentHashMap<Object, Object> servicesCache = new ConcurrentHashMap<Object, Object>();


    public static void init(Prop prop) {
        dubboConfigFactory = new DubboConfigFactory(prop.get("dubbo.application.name"), prop.get("dubbo.registry.address"), prop.get("dubbo.protocol.name"));
        dubboConfigFactory.setApplicationVersionSafety(prop.get("dubbo.application.version"));
        dubboConfigFactory.setApplicationQosPort(prop.getInt("dubbo.application.qos.port"));

        dubboConfigFactory.setRegistryUsernameSafety(prop.get("dubbo.registry.username"));
        dubboConfigFactory.setRegistryPasswordSafety(prop.get("dubbo.registry.password"));

        dubboConfigFactory.setProtocolPortSafety(prop.getInt("dubbo.protocol.port"));
        dubboConfigFactory.setProtocolThreadsSafety(prop.getInt("dubbo.protocol.threads"));
        dubboConfigFactory.setProviderTokenSafety(prop.get("dubbo.provider.token"));


        if (StrKit.notBlank(prop.get("dubbo_base_package"))) {
            dubbo_base_package = prop.get("dubbo_base_package");
            classPath = PathKit.getRootClassPath() + "/" + dubbo_base_package.replace(".", "/");
        } else {
            classPath = PathKit.getRootClassPath();
        }
        if (StrKit.notBlank(prop.get("initLoad"))) {
//            initLoad = prop.getBoolean("initLoad");
        }
    }

    public static <T> void scanServices() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        List<Class> classList = new ArrayList<Class>();
        scanClass(classList, classPath);
        for (Class cl : classList) {
            if (cl.isInterface() || Modifier.isAbstract(cl.getModifiers())) {
                continue;
            }
            exportService(cl);
//            if(initLoad)loadReferenceServices(cl);
        }
    }

    private static <T> void exportService(Class<?> cl) throws InstantiationException, IllegalAccessException {
        Service service = (Service) cl.getAnnotation(Service.class);
        if (service == null) return;

        Class<?>[] interfaces = cl.getInterfaces();
        if (interfaces == null || !(interfaces.length > 0)) {
//            logger.error("A service must implement an interface");
            throw new JFinalDubboPluginException("A service must implement an interface");
        }
        for (Class<?> in : interfaces) {
            ServiceConfig<T> serviceConfig = dubboConfigFactory.createServiceConfig(cl, service, in);
            serviceConfig.export();
            System.out.println("发布成功");
        }
    }

    @SuppressWarnings("rawtypes")
    private static void scanClass(List<Class> classList, String path) throws ClassNotFoundException, IOException {
        File[] files = new File(path).listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory()) {
                    scanClass(classList, file.getAbsolutePath());
                } else if (file.getName().endsWith(".class")) {
                    int start = classPath.length() - dubbo_base_package.length() - 1;
                    int end = file.toString().length() - ".class".length();
                    String classFile = file.toString().substring(start + 1, end);
                    String className = classFile.replace(File.separator, ".");
                    if (className.indexOf(dubbo_base_package) > -1) {
                        classList.add(Class.forName(className));
                    }
                }
            }
        }

    }

    public static <T> T receiveService(Class<T> interfaceClass) {
        return receiveService(interfaceClass, null, null);
    }

    public static <T> T receiveService(Class<T> interfaceClass, String group, String version) {
        Map<String, String> config = new HashMap<String, String>();
        config.put("group", group);
        config.put("version", version);
        return receiveService(interfaceClass, config);
    }

    public static <T> T receiveService(Class<T> interfaceClass, Map<String, String> config) {
        String configKey = JsonKit.toJson(config);
        T service = (T) servicesCache.get(interfaceClass.getSimpleName() + ":" + configKey);
        if (service != null) return service;
        ReferenceConfig<T> referenceConfig = dubboConfigFactory.createReferenceConfig(interfaceClass, config);
        service = referenceConfig.get();
        if (service == null) {
            throw new JFinalDubboPluginException("Did not get rpc:" + interfaceClass.getName());
        }
        servicesCache.put(interfaceClass.getSimpleName() + ":" + configKey, service);
        return service;
    }

    public static Map<String, String> buildPara(Reference reference) {
        Map<String, String> config = new HashMap<String, String>();
        config.put("version", reference.version());
        config.put("group", reference.group());
        config.put("retries", String.valueOf(reference.retries()));
        config.put("cluster", reference.cluster().toString().toLowerCase());
        config.put("stub", reference.stub());
        config.put("mock", reference.mock());
        config.put("loadbalance", reference.loadbalance().toString().toLowerCase());
        config.put("timeout", String.valueOf(reference.timeout()));
        config.put("connections", String.valueOf(reference.connections()));
        config.put("async", String.valueOf(reference.async()));
        config.put("generic", String.valueOf(reference.generic()));
        config.put("check", String.valueOf(reference.check()));
        config.put("url", reference.url());
        config.put("cache", reference.cache());
        config.put("validation", String.valueOf(reference.validation()));
        config.put("proxy", reference.proxy());
        config.put("client", reference.client());
        config.put("owner", reference.owner());
        config.put("actives", String.valueOf(reference.actives()));
        config.put("filter", reference.filter());
        config.put("listener", reference.listener());
        config.put("layer", reference.layer());
        config.put("init", String.valueOf(reference.init()));
        config.put("protocol", reference.protocol());
        return config;
    }

    public static DubboConfigFactory getDubboConfigsFactory() {
        return dubboConfigFactory;
    }

}
